/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import Vue from 'vue'
import Config from '../../config'
import Popper from '../../utils/vue-popper'
import debounce from 'throttle-debounce/debounce'
import { oneOf, generateId, addClass, removeClass, on, off, getFirstComponentChild } from '../../utils/assist'

const clsPrefix = Config.clsPrefix

export default {
  name: `${Config.namePrefix}Tooltip`,

  mixins: [Popper],

  props: {
    delay: {
      type: Number,
      default: 0
    },
    disabled: Boolean,
    manual: Boolean,
    theme: {
      validator (value) {
        return oneOf(value, ['dark', 'light'])
      },
      default: 'dark'
    },
    arrowOffset: {
      type: Number,
      default: 0
    },
    popperClass: String,
    content: String,
    visibleArrow: {
      default: true
    },
    transition: {
      type: String,
      default: 'fade'
    },
    popperOptions: {
      default () {
        return {
          boundariesPadding: 10,
          gpuAcceleration: false
        }
      }
    },
    enterable: {
      type: Boolean,
      default: true
    },
    hideAfter: {
      type: Number,
      default: 0
    }
  },

  data () {
    return {
      clsPrefix: clsPrefix,
      prefixCls: `${clsPrefix}tooltip`,
      timeoutPending: null,
      focusing: false
    }
  },
  computed: {
    tooltipId () {
      return `${this.prefixCls}-${generateId()}`
    }
  },
  beforeCreate () {
    if (this.$isServer) return

    this.popperVM = new Vue({
      data: { node: '' },
      render (h) {
        return this.node
      }
    }).$mount()

    this.debounceClose = debounce(200, () => this.handleClosePopper())
  },

  render (h) {
    if (this.popperVM) {
      this.popperVM.node = (
        <transition
      name={ this.transition }
      onAfterLeave={ this.doDestroy }>
    <div
      onMouseleave={ () => { this.setExpectedState(false); this.debounceClose() } }
      onMouseenter= { () => { this.setExpectedState(true) } }
      ref="popper"
      role="tooltip"
      id={this.tooltipId}
      aria-hidden={ (this.disabled || !this.showPopper) ? 'true' : 'false' }
      v-show={!this.disabled && this.showPopper}
    class={
        [ this.prefixCls + '__popper', 'is-' + this.theme, this.popperClass ]
    }>
      { this.$slots.content || this.content }
    </div>
      </transition>)
    }

    if (!this.$slots.default || !this.$slots.default.length) return this.$slots.default

    const vnode = getFirstComponentChild(this.$slots.default)

    if (!vnode) return vnode

    const data = vnode.data = vnode.data || {}
    data.staticClass = this.concatClass(data.staticClass, this.prefixCls)

    return vnode
  },

  mounted () {
    this.referenceElm = this.$el
    if (this.$el.nodeType === 1) {
      this.$el.setAttribute('aria-describedby', this.tooltipId)
      this.$el.setAttribute('tabindex', 0)
      on(this.referenceElm, 'mouseenter', this.show)
      on(this.referenceElm, 'mouseleave', this.hide)
      on(this.referenceElm, 'focus', () => {
        if (!this.$slots.default || !this.$slots.default.length) {
        this.handleFocus()
        return
      }
      const instance = this.$slots.default[0].componentInstance
      if (instance && instance.focus) {
        instance.focus()
      } else {
        this.handleFocus()
      }
    })
      on(this.referenceElm, 'blur', this.handleBlur)
      on(this.referenceElm, 'click', this.removeFocusing)
    }
  },
  watch: {
    focusing (val) {
      if (val) {
        addClass(this.referenceElm, 'focusing')
      } else {
        removeClass(this.referenceElm, 'focusing')
      }
    }
  },
  methods: {
    show () {
      this.setExpectedState(true)
      this.handleShowPopper()
    },
    hide () {
      this.setExpectedState(false)
      this.debounceClose()
    },
    handleFocus () {
      this.focusing = true
      this.show()
    },
    handleBlur () {
      this.focusing = false
      this.hide()
    },
    removeFocusing () {
      this.focusing = false
    },

    concatClass (a, b) {
      if (a && a.indexOf(b) > -1) return a
      return a ? b ? (a + ' ' + b) : a : (b || '')
    },

    handleShowPopper () {
      if (!this.expectedState || this.manual) return
      clearTimeout(this.timeout)
      this.timeout = setTimeout(() => {
        this.showPopper = true
    }, this.delay)

      if (this.hideAfter > 0) {
        this.timeoutPending = setTimeout(() => {
          this.showPopper = false
      }, this.hideAfter)
      }
    },

    handleClosePopper () {
      if ((this.enterable && this.expectedState) || this.manual) return
      clearTimeout(this.timeout)

      if (this.timeoutPending) {
        clearTimeout(this.timeoutPending)
      }
      this.showPopper = false

      if (this.disabled) {
        this.doDestroy()
      }
    },

    setExpectedState (expectedState) {
      if (expectedState === false) {
        clearTimeout(this.timeoutPending)
      }
      this.expectedState = expectedState
    }
  },

  destroyed () {
    const reference = this.referenceElm
    off(reference, 'mouseenter', this.show)
    off(reference, 'mouseleave', this.hide)
    off(reference, 'focus', this.handleFocus)
    off(reference, 'blur', this.handleBlur)
    off(reference, 'click', this.removeFocusing)
  }
}
